// 版权所有2009 Go作者。版权所有。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// 固定大小的对象分配器。返回的内存未归零。
// 
// 请参阅malloc.go以了解概述。

package runtime

import "unsafe"

// FixAlloc是一个用于固定大小对象的简单自由列表分配器。
// Malloc使用围绕sysAlloc的FixAlloc来管理其
// mcache和mspan对象。
// 
// fixalloc.alloc返回的内存默认为零，但是
// 调用者可以通过将
// 零标志设置为false来负责将分配归零。只有当内存从不包含堆指针时，这才是安全的。
// 
// 调用方负责锁定FixAlloc调用。
// 调用者可以在对象中保留状态，但第一个字是
// 通过释放和重新分配而崩溃。ABCFDG 
type fixalloc struct {
	size   uintptr
	first  func(arg, p unsafe.Pointer) // 第一次返回p时调用
	arg    unsafe.Pointer
	list   *mlink
	chunk  uintptr // 使用uintptr而不是不安全的。避免写入障碍的指针
	nchunk uint32
	inuse  uintptr // 正在使用的字节
	stat   *sysMemStat
	zero   bool // 零分配
}

// 块的通用链接列表。（通常块大于sizeof（MLink）。）
// 因为分配给MLink.next将导致执行写屏障
// 这不能由某些内部GC结构使用。例如，当清理程序将一个未标记的对象放在自由列表上时，它不希望调用
// 写屏障，因为这可能导致对象可访问。
// 
// go:notinheap 
type mlink struct {
	next *mlink
}

// 初始化f以分配给定大小的对象，
// 使用分配器获取内存块。
func (f *fixalloc) init(size uintptr, first func(arg, p unsafe.Pointer), arg unsafe.Pointer, stat *sysMemStat) {
	f.size = size
	f.first = first
	f.arg = arg
	f.list = nil
	f.chunk = 0
	f.nchunk = 0
	f.inuse = 0
	f.stat = stat
	f.zero = true
}

func (f *fixalloc) alloc() unsafe.Pointer {
	if f.size == 0 {
		print("runtime: use of FixAlloc_Alloc before FixAlloc_Init\n")
		throw("runtime: internal error")
	}

	if f.list != nil {
		v := unsafe.Pointer(f.list)
		f.list = f.list.next
		f.inuse += f.size
		if f.zero {
			memclrNoHeapPointers(v, f.size)
		}
		return v
	}
	if uintptr(f.nchunk) < f.size {
		f.chunk = uintptr(persistentalloc(_FixAllocChunk, 0, f.stat))
		f.nchunk = _FixAllocChunk
	}

	v := unsafe.Pointer(f.chunk)
	if f.first != nil {
		f.first(f.arg, v)
	}
	f.chunk = f.chunk + f.size
	f.nchunk -= uint32(f.size)
	f.inuse += f.size
	return v
}

func (f *fixalloc) free(p unsafe.Pointer) {
	f.inuse -= f.size
	v := (*mlink)(p)
	v.next = f.list
	f.list = v
}
